/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package main.KBProcess;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import main.KBProcess.Element.ElementType;
import main.NER.Trie.ContextTrie.ORIENTATION;
import main.Ontology.ElementOntologyFactory;
import main.initiate.UTEES_Index;

/**
 *
 * @author Isaac Osesina
 */
public class DefaultKnowledgeBaseDocument implements Serializable, Cloneable, KnowledgeBaseDocument {

    //Data Members
    int docID;
    String docText;
    String docContext;
    int entityCnt;
    ArrayList<Element> elementList;
    UTEES_MatchTable documentOntology;
    int entitySequenceNumInDoc;
    
    private ElementOntologyFactory elementOntologyFactory;

    public DefaultKnowledgeBaseDocument(int docID, UTEES_MatchTable documentOntology, ElementOntologyFactory elementOntologyFactory) {
        this.docID = docID;
        this.documentOntology = documentOntology;
        entitySequenceNumInDoc = 0;
        elementList = new ArrayList<Element>();
        this.elementOntologyFactory = elementOntologyFactory;
    }

    public DefaultKnowledgeBaseDocument() {
    }

//    public final void setDocID(int docID) {this.docID = docID;}
//    public final void setDocType(String DocType) {this.documentCategory = DocType;}
    @Override
    public void setDocText(String docText) {
        this.docText = docText;
    }

    @Override
    public void setDocContext(String docContext) {
        this.docContext = docContext;
    }

        /**
     * Returns text contained in document
     * @param docID: id of document
     * @return
     */
    @Override
    public StringBuilder getKBDocTextTagged() {
        return getKBDocTextTagged(elementList);
    }

    /**
     *  Returns text contained in given KB elements
     * @param elements
     * @return
     */
    @Override
    public StringBuilder getKBDocTextTagged(List<Element> elements) {
        StringBuilder strBuild = null;
        if(elements != null && !elements.isEmpty()) {
            strBuild = new StringBuilder();
            strBuild.append(UTEES_Index.DOCUMENT_START_PAD);
            for(Element e:elements) {
                if(e != null) {
                    ElementType type = e.getType();
                    switch (type) {
                        case CONTEXT:
                        case FILLER:
                            strBuild.append(e.getValue());
                            break;
                        case ENTITY:
                            String tag = UTEES_Index.tag(e.getLabelIndex());
                            strBuild.append(tag);
                            break;
                    }
                }
            }
            strBuild.append(UTEES_Index.DOCUMENT_END_PAD); 
        }
        return strBuild;
    }
    
    @Override
    public List<Element> getElementsBeforeFragment(Element element){        
        if(elementList!= null && elementList.contains(element)) {
            int elementPos = elementList.indexOf(element);
            List<Element> subList = elementList.subList(0, elementPos);
            return subList;
        }
        return null;
    }

    @Override
    public StringBuilder getDocTextTagged_BeforeFragment(Element element){
        List<Element> elements = getElementsBeforeFragment(element);
        if(elements == null) {
            return new StringBuilder(UTEES_Index.DOCUMENT_START_PAD);
        }
        return getKBDocTextTagged(elements);
    }

    @Override
    public List<Element> getElementsAfterFragment(Element element){        
        if(elementList!= null && elementList.contains(element)) {
            int elementPos = elementList.indexOf(element);
            List<Element> subList = elementList.subList(elementPos + 1, elementList.size());
            return subList;
        }
        return null;
    }

    @Override
    public StringBuilder getDocTextTagged_AfterFragment(Element element){
        List<Element> elements = getElementsAfterFragment(element);
        if(elements == null) {
            return new StringBuilder(UTEES_Index.DOCUMENT_END_PAD);
        }
        return getKBDocTextTagged(elements);
    }

    @Override
    public void setEntityCnt(int entityCnt) {
        this.entityCnt = entityCnt;
    }


    @Override
    public int getDocID() {
        return docID;
    }


    @Override
    public String getDocType() {
        return this.getLastElement() == null ? null:getLastElement().getDocumentCategory();
    }
    
    @Override
    public String getDocLanguage() {
        return this.getLastElement() == null ? null:getLastElement().getDocumentLanguage();
    }


    @Override
    public String getDocText() {
        return docText;
    }


    @Override
    public int getEntityCnt() {
        return entityCnt;
    }


    @Override
    public int getElementCnt() {
        return elementList.size();
    }


    @Override
    public Element getFirstElement() {
        return elementList.get(0);
    }


    @Override
    public Element getElement(int i) {
        return elementList.get(i);
    }


    @Override
    public List<Element> getEntities() {
        if(elementList != null && elementList.size()>0) {
            List<Element> entityList = new ArrayList<Element>();
            for(int i=1; i<elementList.size(); i=i+2) {                
                entityList.add(elementList.get(i));                
            }
            return entityList;
        }
        return null;
    }


    @Override
    public List<Element> getElements() {
        return elementList;
    }


    @Override
    public Element getLastElement() {
        if (elementList == null || elementList.isEmpty()) {
            return null;
        }
        return elementList.get(elementList.size() - 1);
    }

    @Override
    public Fragment getLastFragment() {
        List<Fragment> fragmentList = getFragmentList();
        if (fragmentList == null || fragmentList.isEmpty()) {
            return null;
        }
        return fragmentList.get(fragmentList.size() - 1);
    }

    
    public void addElement(Element element) {
        if(element==null)
            return;
            
            if(elementList==null) {
                elementList = new ArrayList<Element>();
            }
            //if entitytype is not to be indexed
            if (!documentOntology.findMatch(element.getDocumentLanguage() + element.getDocumentCategory() + "." + element.getLabel())) {
                element.setType(ElementType.CONTEXT);
            }
            Element lastElement = this.getLastElement();
            if(lastElement != null) {
                ElementType type = lastElement.getType();
                switch (type) {
                    case ENTITY:
                        if(element.getType()==ElementType.ENTITY) {                            
                            Element padElement = createPad(UTEES_Index.ELEMENT_PAD);
                            addElement(padElement);
                        }
                        break;
                    case CONTEXT:
                        if(element.getType()==ElementType.CONTEXT) {
                            lastElement.setValue( lastElement.getValue() + element.getValue() );
                            return;
                        }
                        if(element.getType()==ElementType.FILLER) {
                            return;
                        }
                        break;
                    case FILLER:
                        if(element.getType()==ElementType.FILLER) {
                            return;
                        }
                        if(element.getType()==ElementType.CONTEXT) {
                            elementList.remove(lastElement);
                        }
                        break;
                    default:
                        //do nothing
                }
            }
            //check if first element will be an entity
            else if(element.getType() == ElementType.ENTITY) {
                Element padElement;
                if(elementList==null || elementList.isEmpty()) {
                    padElement = createPad(UTEES_Index.DOCUMENT_START_PAD);
                }
                else {
                    padElement = createPad(UTEES_Index.ELEMENT_PAD);
                }
                addElement(padElement);
            }
                            
            element.setSequenceInDoc(elementList.size());
            
            //adjust index of element which may have changed because filler(s) were added to the elements.            
            if(lastElement != null) {
                element.setPreElement(lastElement);
                lastElement.setPostElement(element);
                element.setIndexOf( lastElement.getIndexOf()+lastElement.getValue().length() );
            }
            else {
                element.setIndexOf(0);
            }
            elementList.add(element);
        
    }
    

    @Override
    public List<Fragment> getFragmentList() {
        List<Fragment> fragmentList = null;
        if(elementList!=null) {
            Element lastElement = this.getLastElement();
            if(lastElement!=null && lastElement.getType()==ElementType.ENTITY) {
                addElement( createPad(UTEES_Index.DOCUMENT_END_PAD) );
            }
            fragmentList = new ArrayList<Fragment>();
            try{
                for(int i=2; i<elementList.size(); i=i+2) {
                    Fragment f = new DefaultFragment();
                    f.setSequenceNum(fragmentList.size());
                    f.setLeftElement(elementList.get(i-2));
                    f.setEntityElement(elementList.get(i-1));
                    f.setRightElement(elementList.get(i));
                    fragmentList.add(f);
                }
            }
            catch(Exception e) {
                System.err.print("");
                fragmentList = null;
            }
        }
        return fragmentList;
    }

    private Element createPad(String padValue) {
        Element padElement = new DefaultElement(this.elementOntologyFactory);
        padElement.setLabel(Element.CONTEXT);
        padElement.setType(ElementType.FILLER);
        padElement.setValue(padValue);
        padElement.setIndexOf(-1);
        padElement.setLabelIndex(-1);
        padElement.setDocID(docID);
        return padElement;
    }
    

    @Override
	public KnowledgeBaseDocument clone() {
        try {
            return (KnowledgeBaseDocument) super.clone();
        } catch (CloneNotSupportedException ex) {
            throw new InternalError();
        }
    }

    @Override
    public int countStringOccurence(String string, ORIENTATION orientation)
    {
        int freq = 0;

        StringBuilder kBDocTextTagged = getKBDocTextTagged();
        if(kBDocTextTagged == null)
            return freq;
        if(orientation == ORIENTATION.LEFT) {
            kBDocTextTagged = kBDocTextTagged.reverse();
        }        
        if(string != null) {
            Matcher m = Pattern.compile(string, Pattern.LITERAL).matcher(kBDocTextTagged.toString());
            while(m.find()) {
                freq++;
            }
        }
//        System.out.println("DocID:" + docID +", freq:" + freq);
        return freq;
    }
}
